package com.tangruojie.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tangruojie.converter.UserConverter;
import com.tangruojie.domain.Code;
import com.tangruojie.domain.Result;
import com.tangruojie.domain.dto.UserDTO;
import com.tangruojie.domain.entity.User;
import com.tangruojie.domain.query.UserQuery;
import com.tangruojie.domain.vo.PageVO;
import com.tangruojie.domain.vo.UserVO;
import com.tangruojie.service.IUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 对比项	        @Autowired 注入	                @RequiredArgsConstructor 注入
 * 注入方式	        字段注入（也可以用于 setter）	    构造器注入
 * 依赖注入原理	    Spring 通过反射赋值	            Spring 通过构造方法参数传入
 * 是否可变	        变量非 final，可变	            final 强制不可变（推荐）
 * 是否易于测试	    ❌ 不易 mock	                ✅ 易于 mock 单元测试
 * 代码简洁度	        写法多，冗余	                    更简洁，配合 Lombok
 * 空指针风险	        可能被注入失败	                    构造器注入能确保依赖必填
 * Spring 推荐程度	不推荐字段注入	                    ✅ 官方推荐构造器注入（最安全）
 * ----------
 * Spring 官方推荐使用：构造器注入
 * Spring 官方文档建议使用构造器注入，因为它更容易测试、更加稳定和安全。
 */
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {

    /**
     * 在 Java 中，final 用于修饰字段意味着：
     * 这个字段只能被赋值一次，且赋值后不可更改。
     * 也就是说，一旦 userService 被注入，就不能被替换或置为 null，具备只读性、线程安全性，这正是我们在依赖注入中希望的行为。
     * ----------
     * 加了 final，就表明：
     * ❗这个依赖是 强依赖（Required），构造对象时必须提供，不能省略。
     * 配合 Lombok 的 @RequiredArgsConstructor，它会自动为所有 final 字段生成构造方法参数，实现自动注入。
     */

    private final UserConverter userConverter;

    private final IUserService userService;

    /**
     * 新增用户
     */
    @PostMapping
    public Result saveUser(@RequestBody UserDTO userDTO) {
        User user = userConverter.toUser(userDTO);
        boolean save = userService.save(user);
        return new Result(
                save ? Code.SAVE_SUCCESS : Code.SAVE_ERROR,
                save ? "保存成功" : "保存失败",
                save
        );
    }

    /**
     * 删除用户
     */
    @DeleteMapping("/{id}")
    public Result deleteUserById(@PathVariable Long id) {
        boolean delete = userService.removeById(id);
        return new Result(
                delete ? Code.DELETE_SUCCESS : Code.DELETE_ERROR,
                delete ? "删除成功" : "删除失败",
                delete
        );
    }

    /**
     * 根据id查询用户
     */
    @GetMapping("/{id}")
    public Result getUserById(@PathVariable Long id) {
        User user = userService.getById(id);
        System.out.println("user = " + user);
        UserVO userVO = userConverter.toUserVO(user);
        System.out.println("userVO = " + userVO);
        return new Result(
                userVO != null ? Code.GET_SUCCESS : Code.GET_ERROR,
                userVO != null ? "查询成功" : "查询失败",
                userVO
        );
    }

    /**
     * 比较维度	        @RequestParam	                            @RequestBody
     * 数据来源	        来自 URL 参数，如 ?ids=1&ids=2	            来自请求体（Request Body）
     * 请求方法	        主要用于 GET/POST（但不限定）	                主要用于 POST/PUT
     * 适合数据	        简单类型：int、String、List（通过 URL 编码传）	复杂结构：对象、List、Map（通过 JSON/XML）
     * 传输格式	        application/x-www-form-urlencoded	        application/json（默认）
     * 编码方式	        URL 编码，如 ?ids=1&ids=2&ids=3	            JSON 格式，如 [1, 2, 3]
     * Swagger 支持	    ✅ GET 可用，无报错	                        ❌ GET 下报错，必须用 POST
     * 推荐使用场景	    查询参数、表单	                                数据提交、搜索条件、对象结构
     * ----------
     * 情况	                    会报错吗？
     * GET + @RequestBody	    ❌ Swagger 报错，浏览器阻止，HTTP 标准不允许 GET 有 body
     * POST + @RequestParam	    ✅ 正常使用，但仅适合表单参数（如 loginForm）
     * POST + @RequestBody	    ✅ 推荐做法，标准 JSON 提交方式
     */

    /**
     * 根据id批量查询
     */
    @GetMapping
    public Result getUserByIds(@RequestParam List<Long> ids) {
        List<UserVO> listUserVO = userService.listByIds(ids).stream()
                .map(userConverter::toUserVO)
                .toList();
        return new Result(
                Code.GET_SUCCESS,
                "查询成功",
                listUserVO
        );
    }

    /**
     * 查询所有用户
     */
    @GetMapping("/all")
    public Result getUserAll() {
        List<UserVO> listUserVO = userService.list().stream()
                .map(userConverter::toUserVO)
                .toList();
        return new Result(
                Code.GET_SUCCESS,
                "查询成功",
                listUserVO
        );
    }

    /**
     * @ModelAttribute 是 Spring MVC 提供的参数绑定注解，作用是：
     * 从请求参数中提取值，绑定到方法参数或模型对象上，然后注入到 Controller 方法中。
     * ----------
     * Spring MVC 的默认规则：
     * 如果方法参数是：
     * JavaBean 对象（如 UserQuery）
     * 没有任何注解
     * 那么它就会使用：@ModelAttribute 方式进行绑定（从 URL 参数 → JavaBean）
     */

    /**
     * 条件查询用户
     */
    @GetMapping("/query")
    public Result queryUser(@ModelAttribute UserQuery userQuery) {
        return new Result(
                Code.GET_SUCCESS,
                "查询成功",
                userService.queryUser(userQuery)
        );
    }

    /**
     * 根据id更新年龄
     */
    @PutMapping("/{id}/age/{age}")
    public Result updateUserAgeById(@PathVariable Long id, @PathVariable Integer age) {
        boolean update = userService.updateUserAgeById(id, age);
        return new Result(
                update ? Code.UPDATE_SUCCESS : Code.UPDATE_ERROR,
                update ? "更新成功" : "更新失败",
                update
        );
    }

    /**
     * 更新用户
     */
    @PutMapping
    public Result updateUser(@RequestBody UserDTO userDTO) {
        User user = userConverter.toUser(userDTO);
        boolean update = userService.updateById(user);
        return new Result(
                update ? Code.UPDATE_SUCCESS : Code.UPDATE_ERROR,
                update ? "更新成功" : "更新失败",
                update
        );
    }

    /**
     * 分页查询用户
     */
    @GetMapping("/page")
    public Result pageUser(@ModelAttribute UserQuery userQuery) {
        PageVO<UserVO> pageVO = userService.queryUserPage(userQuery);
        return new Result(
                Code.GET_SUCCESS,
                "查询成功",
                pageVO
        );
    }

}
